/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package my.cbr.utils;

import my.cbr.database.model.ApproximationNumber;

/**
 *
 * @author Geir Ole
 */
public class LinearApproximation{
    private ApproximationNumber min = null;
    private ApproximationNumber max = null;
    public double minimum = 0;
    public double maximum = 0;
    private double part = 0;

    public LinearApproximation()
    {
        
    }
    public LinearApproximation(double min, double max){
         maximum = max;
         minimum = min;
         double comb = maximum - minimum;
         part = comb/5;
     }

     public void createMeasurementAbstractions(){
         maximum = max.getB();
         minimum = min.getB();
         double comb = maximum - minimum;
         part = comb/5;
     }


     public String getTrend(ApproximationNumber approximationNumbers){
         if(approximationNumbers==null)
             return "Unknown";
         double bx = approximationNumbers.getB();         

         if(bx < minimum+part){
                return "Decreasing";
            }
            else if((bx >= minimum+part) && (bx < minimum+(part*2))){
                return "DecreasingWeakly";
            }
            else if((bx >= minimum+(part*2)) && (bx < minimum+(part*3))){
                return "Stable";
            }
            else if((bx >= minimum+(part*3)) && (bx < minimum+(part*4))){
                return "IncreasingWeakly";
            }
            else{
                return "Increasing";
            }
     }

    /**
     *
     * @param x
     * @param y
     * @throws IllegalArgumentException
     */
    public ApproximationNumber applyLinearApproximation(Double[] x, Double[] y) throws IllegalArgumentException {
        int N = 0;
        Double a = 0.0;
        Double b = 0.0;

        if(x.length!=y.length) {
            throw new IllegalArgumentException("Arrays are not of same size: x["+x.length+"] : y["+y.length+"]");
        }
        else
            N = x.length;
        Double sumX = this.summarize(x, 1);
        Double sumY = this.summarize(y, 1);
        Double sumXY = this.sumXY(x, y);
        Double sumXSquared = this.summarize(x, 2);

        b = this.calculateB(new Double(N), sumXY, sumX, sumY, sumXSquared);
        a = this.calculateA(sumY, b, sumX, N);
        
        ApproximationNumber approximationNumber = new ApproximationNumber();
        approximationNumber.setA(a);
        approximationNumber.setB(b);

        // Check to see if max and min have been initialized
        if(max == null && min == null){
            max = approximationNumber;
            min = approximationNumber;
        }
        else{
            if(b > max.getB()){
                max = approximationNumber;
            }
            if(b < min.getB()){
                min = approximationNumber;
            }
        }
        return approximationNumber;
    }

    public ApproximationNumber applyLinearApproximation(Double[] y) {
        if(y.length==0)
            return null;
        Double[] x = new Double[y.length];
        for(int i = 0; i < y.length; i++)
            x[i] = new Double(i);
        return this.applyLinearApproximation(x, y);
    }

    /**
     *
     * @param sumY
     * @param b
     * @param sumX
     * @param N
     * @return
     */
    private Double calculateA(Double sumY, Double b, Double sumX, int N) {
        Double sum = 0.0;
        sum = (sumY - (b*sumX))/N;
        return sum;
    }

    /**
     *
     * @param N
     * @param sumXY
     * @param sumX
     * @param sumY
     * @param sumXSquared
     * @return
     */
    private Double calculateB(Double N, Double sumXY, Double sumX, Double sumY, Double sumXSquared) {
        Double sum = 0.0;
        sum = ((N*sumXY)-(sumX*sumY))/(N*sumXSquared - (sumX*sumX));
        return sum;
    }

    /**
     *
     * @param values
     * @param power
     * @return
     */
    private Double summarize(Double[] values, int power) {
        Double sum = 0.0;
        for(Double v:values)
            sum += Math.pow(v, power);
        return sum;
    }

    @Override
    public String toString() {
        return "LinearRegression{" + "min=" + min + " max=" + max + " minimum=" + minimum + " maximum=" + maximum + " part=" + part + '}';
    }

    /**
     *
     * @param x
     * @param y
     * @return
     * @throws IllegalArgumentException
     */
    private Double sumXY(Double[] x, Double[] y) throws IllegalArgumentException {
        Double sum = 0.0;
        if(x.length!=y.length)
            throw new IllegalArgumentException("Arrays are not of same size: x["+x.length+"] : y["+y.length+"]");
        for(int i = 0; i < x.length; i++)
            sum += x[i]*y[i];
        return sum;
    }

    public static void main(String[] args) {
        LinearApproximation lr = new LinearApproximation();
        Double[] x = {1d,2d,3d,4d,5d,6d,7d,8d,9d,10d,11d,12d,13d};
        Double[] y = {1d,2d,2d,3d,5d,4d,6d,7d,8d,7d,8d,8d,8d};
//        Double[] x = {0,1,2,3,4};
//        Double[] y = {0,2,4,1,3};
        lr.applyLinearApproximation(x, y);
        System.out.println(lr);
        Double[] x2 = {1d,2d,3d,4d,5d,6d};
        Double[] y2 = {1d,2d,3d,4d,5d};
        lr.applyLinearApproximation(x2, y2);
        System.out.println(lr);

    }
}
